﻿Imports System
Imports System.IO
Imports System.Text
Imports SaveFormatInterface
Imports SaveFormatInterface.Interfaces
Public Class ConversionOption
    Public Property SourceFileName As String
    Public Property SourceFormat As Interfaces.SaveFormat
    Public Property TargetFormat As Interfaces.SaveFormat
    Public Property TargetFileName As String
    Public Property TargetSize As Integer
    Public Property ARDSGameName As String
    Public Property ARDSSaveName As String
    Public Property ARDSDescription As String
    Public Sub New(ByVal SourceFile As String, ByVal SourceSaveFormat As Interfaces.SaveFormat, ByVal TargetSaveFormat As Interfaces.SaveFormat, ByVal TargetFile As String, ByVal TargetSaveSize As Long, ByVal ARDSExtGameName As String, ByVal ARDSExtSaveName As String, ByVal ARDSExtSaveDescription As String)
        SourceFileName = SourceFile
        SourceFormat = SourceSaveFormat
        TargetFormat = TargetSaveFormat
        TargetFileName = TargetFile
        TargetSize = TargetSaveSize
        ARDSGameName = ARDSExtGameName
        ARDSSaveName = ARDSExtSaveName
        ARDSDescription = ARDSExtSaveName
    End Sub
End Class
Public Class Converter
    Public Shared ReadOnly Property EmptyIntArray As Long()
        Get
            Dim output As Long() = {}
            Return output
        End Get
    End Property
    Dim _local As Boolean
    Public Shared Function Convert(ByVal Input As Byte(), ByVal SourceFormat As Interfaces.SaveFormat, ByVal TargetFormat As Interfaces.SaveFormat, ByVal TargetSize As Long) As Byte()
        Dim output As Byte() = {}
        output = TargetFormat.ToType(Resize(SourceFormat.ToRaw(Input), TargetSize))
        Return output
    End Function
    Public Shared Function GetSaveFormats(ByVal FormatType As String) As Generic.List(Of Interfaces.SaveFormat)
        Dim output As New Generic.List(Of Interfaces.SaveFormat)
        'Load Internal
        Dim collections As New List(Of Interfaces.SaveFormatCollection)
        Select Case FormatType.ToLower
            Case "nds"
                collections.Add(New RawFormatCollection)
                collections.Add(New R4FormatCollection)
                collections.Add(New DeSmuMe.Formats)
                collections.Add(New ARDS.Formats)
            Case Else
                collections.Add(New SingleRawFormatCollection)
        End Select
        'Load External collections
        If IO.Directory.Exists(Path.Combine(Environment.CurrentDirectory, "plugins/" & FormatType)) Then
            Dim plugins As String() = IO.Directory.GetFiles(Path.Combine(Environment.CurrentDirectory, "plugins/" & FormatType), "*.dll")
            For Each plugin In plugins
                Dim a As System.Reflection.Assembly = System.Reflection.Assembly.LoadFrom(plugin)
                Dim types As Type() = a.GetTypes
                For Each item In types
                    Dim IsCollection As Boolean = False
                    For Each intface As Type In item.GetInterfaces
                        If intface Is GetType(SaveFormatCollection) Then
                            IsCollection = True
                            Exit For
                        End If
                    Next
                    If IsCollection Then
                        Dim CurrentCollection As SaveFormatCollection = a.CreateInstance(item.ToString)
                        collections.Add(CurrentCollection)
                    End If
                Next
            Next
        End If
        'Combine collections
        For Each collection In collections
            For Each item In collection.SaveFormats
                output.Add(item)
            Next
        Next
        Return output
    End Function
    Public Shared Function GetSaveFormatByFriendlyName(ByVal FriendlyName As String, FormatType As String) As Interfaces.SaveFormat
        For Each item In GetSaveFormats(FormatType)
            If item.Name = FriendlyName Then
                Return item
                Exit Function
            End If
        Next
        Return New RawFormat
    End Function
    ''' <summary>
    ''' Returns Custom Size if Format Size is 0.
    ''' </summary>
    ''' <param name="FormatSize">Size the save format uses</param>
    ''' <param name="CustomSize">Size to resize to if save format has no strict size</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function Size(ByVal FormatSize As Long, ByVal CustomSize As Long) As Long
        If FormatSize = 0 Then
            Return CustomSize
        Else
            Return FormatSize
        End If
    End Function
    ''' <summary>
    ''' Returns an array of byte fill of 1's of a specified size
    ''' </summary>
    ''' <param name="Length">Amount of bytes to return</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function Blank(ByVal Length As Long) As Byte()
        Dim val(Length - 1) As Byte
        For count As Long = 0 To Length - 1
            val(count) = 255
        Next
        Return val
    End Function
    ''' <summary>
    ''' Resizes a byte array to the specified length
    ''' </summary>
    ''' <param name="RAW">Byte array to resize</param>
    ''' <param name="TargetLength">Amount of bytes to resize to</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function Resize(ByVal RAW As Byte(), ByVal TargetLength As Long) As Byte()
        If TargetLength = 0 Then
            TargetLength = DetectLength(RAW)
        End If
        If RAW.LongLength = TargetLength Then
            Return RAW
        Else
            Dim b As Byte() = Blank(TargetLength)
            For count As Long = 0 To TargetLength - 1
                If (RAW.LongLength - 1) >= count Then
                    b(count) = RAW(count)
                End If
            Next
            ' ReDim Preserve output(TargetLength - 1)

            Return b
        End If
    End Function
    ''' <summary>
    ''' Attempts to detect the length of the save file
    ''' </summary>
    ''' <param name="RAW">Save file to detect the length of</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function DetectLength(ByVal RAW As Byte()) As Long
        Dim position As Long
        For count As Long = RAW.LongLength - 1 To 0 Step -1
            If Not RAW(count) = 255 Then
                position = count + 1
                Exit For
            End If
        Next
        If (position > (128 * 1024 ^ 2)) Then
            'File size is greater than supported.  Don't resize
            Return RAW.LongLength
        ElseIf (position = (128 * 1024 ^ 2)) Then '128MB
            Return (128 * 1024 ^ 2)
        ElseIf (64 * 1024 ^ 2) < position And position < (128 * 1024 ^ 2) Then
            Return (128 * 1024 ^ 2)
        ElseIf (position = (64 * 1024 ^ 2)) Then '64MB
            Return (64 * 1024 ^ 2)
        ElseIf (32 * 1024 ^ 2) < position And position < (64 * 1024 ^ 2) Then
            Return (64 * 1024 ^ 2)
        ElseIf (position = (32 * 1024 ^ 2)) Then '32MB
            Return (32 * 1024 ^ 2)
        ElseIf (16 * 1024 ^ 2) < position And position < (32 * 1024 ^ 2) Then
            Return (32 * 1024 ^ 2)
        ElseIf (position = (16 * 1024 ^ 2)) Then '16MB
            Return (16 * 1024 ^ 2)
        ElseIf (8 * 1024 ^ 2) < position And position < (16 * 1024 ^ 2) Then
            Return (16 * 1024 ^ 2)
        ElseIf (position = (8 * 1024 ^ 2)) Then '8MB
            Return (8 * 1024 ^ 2)
        ElseIf (4 * 1024 ^ 2) < position And position < (8 * 1024 ^ 2) Then
            Return (8 * 1024 ^ 2)
        ElseIf (position = (4 * 1024 ^ 2)) Then '4MB
            Return (4 * 1024 ^ 2)
        ElseIf (2 * 1024 ^ 2) < position And position < (4 * 1024 ^ 2) Then
            Return (4 * 1024 ^ 2)
        ElseIf (position = (2 * 1024 ^ 2)) Then '2MB
            Return (2 * 1024 ^ 2)
        ElseIf (1024 ^ 2) < position And position < (2 * 1024 ^ 2) Then
            Return (2 * 1024 ^ 2)
        ElseIf (position = (1024 * 1024)) Then '1MB
            Return (1024 ^ 2)
        ElseIf (512 * 1024) < position And position < (1024 * 1024) Then
            Return (1024 ^ 2)
        ElseIf (position = (512 * 1024)) Then '512KB
            Return (512 * 1024)
        ElseIf (256 * 1024) < position And position < (512 * 1024) Then
            Return (512 * 1024)
        ElseIf (position = (256 * 1024)) Then '256KB
            Return (256 * 1024)
        ElseIf ((128 * 1024) < position And position < (256 * 1024)) Then
            Return (256 * 1024)
        ElseIf (position = (128 * 1024)) Then '128KB
            Return (128 * 1024)
        ElseIf ((64 * 1024) < position And position < (128 * 1024)) Then
            Return (128 * 1024)
        ElseIf (position = (64 * 1024)) Then '64KB
            Return (64 * 1024)
        ElseIf ((32 * 1024) < position And position < (64 * 1024)) Then
            Return (64 * 1024)
        ElseIf (position = (32 * 1024)) Then '32KB
            Return (32 * 1024)
        ElseIf ((8 * 1024) < position And position < (32 * 1024)) Then
            Return (32 * 1024)
        ElseIf (position = (8 * 1024)) Then '8KB
            Return (8 * 1024)
        ElseIf ((512) < position And position < (8 * 1024)) Then
            Return (8 * 1024)
        ElseIf (position <= (512)) Then '0.5 KB
            Return (512)
        Else
            'Something went wrong.  No resize should occur
            Return (RAW.LongLength)
        End If
    End Function
    Public Sub New(ByVal Local As Boolean)
        _local = Local
    End Sub
    'Public Class RawFormat
    '    Implements SaveFormat
    '    Public Function ToRaw(ByVal Type As Byte()) As Byte() Implements SaveFormat.ToRaw
    '        Return Type
    '    End Function
    '    Public Function ToType(ByVal RAW As Byte()) As Byte() Implements SaveFormat.ToType
    '        Return RAW
    '    End Function
    '    Property IsOnline As Boolean Implements SaveFormat.IsOnline
    '    Property Filename As String Implements SaveFormat.Filename
    '    Property ExtendedData As String Implements SaveFormat.ExtendedData
    '    Public ReadOnly Property FileExtension As String Implements SaveFormat.FileExtension
    '        Get
    '            Return "sav"
    '        End Get
    '    End Property
    '    Public ReadOnly Property FileSize As Integer Implements SaveFormat.FileSize
    '        Get
    '            Return 0
    '        End Get
    '    End Property
    '    Public ReadOnly Property FriendlyName As String Implements SaveFormat.FriendlyName
    '        Get
    '            Return "Raw Format"
    '        End Get
    '    End Property
    '    Public ReadOnly Property FormatIdentifier As String Implements SaveFormat.FormatIdentifier
    '        Get
    '            Return "raw"
    '        End Get
    '    End Property
    '    Public ReadOnly Property UniqueIdentifier As String Implements SaveFormat.UniqueIdentifier
    '        Get
    '            Return "baseraw"
    '        End Get
    '    End Property
    'End Class
    Public Class RawFormat
        Implements SaveFormat
        Public Property ExtendedData As String Implements SaveFormat.ExtendedData
        Private _fileextension As String
        Public ReadOnly Property FileExtension As String Implements SaveFormat.FileExtension
            Get
                Return _fileextension
            End Get
        End Property
        Public Property Filename As String Implements SaveFormat.Filename
        Private _filesize As Long()
        Public ReadOnly Property FileSize As Long() Implements SaveFormat.FileSize
            Get
                Return _filesize
            End Get
        End Property
        Private _fname As String
        Public ReadOnly Property Name As String Implements SaveFormat.Name
            Get
                Return _fname
            End Get
        End Property
        Public Property IsOnline As Boolean Implements SaveFormat.IsOnline
        Public Function ToRaw(ByVal Type() As Byte) As Byte() Implements SaveFormat.ToRaw
            Return Type
        End Function
        Public Function ToType(ByVal RAW() As Byte) As Byte() Implements SaveFormat.ToType
            Return RAW
        End Function
        Public ReadOnly Property CanBeSource As Boolean Implements Interfaces.SaveFormat.CanBeSource
            Get
                Return True
            End Get
        End Property
        Public ReadOnly Property CanBeTarget As Boolean Implements Interfaces.SaveFormat.CanBeTarget
            Get
                Return True
            End Get
        End Property
        Public Overrides Function ToString() As String Implements SaveFormat.ToString
            Return Name
        End Function
        Public Sub New(ByVal Name As String, ByVal FileExtension As String, ByVal FileSize As Long())
            _fileextension = FileExtension
            _fname = Name
            _filesize = FileSize
        End Sub
        Public Sub New()
            Me.New("Raw Format", "sav", {})
        End Sub
    End Class
    Public Class SingleRawFormatCollection
        Implements SaveFormatCollection

        Public ReadOnly Property SaveFormats As System.Collections.Generic.List(Of SaveFormat) Implements SaveFormatCollection.SaveFormats
            Get
                Dim output As New System.Collections.Generic.List(Of SaveFormat)
                output.Add(New RawFormat)
                Return output
            End Get
        End Property

    End Class
    Public Class RawFormatCollection
        Implements SaveFormatCollection

        Public ReadOnly Property SaveFormats As System.Collections.Generic.List(Of SaveFormat) Implements SaveFormatCollection.SaveFormats
            Get
                Dim output As New System.Collections.Generic.List(Of SaveFormat)
                output.Add(New RawFormat)
                output.Add(New RawFormat("Acekard R.P.G.", "sav", {}))
                output.Add(New RawFormat("Acekard 2 (i)", "sav", {}))
                output.Add(New RawFormat("Supercard DSONE (i)", "sav", {}))
                output.Add(New RawFormat("Supercard DSTWO", "sav", {}))
                output.Add(New RawFormat("EZ Flash V (i)", "sav", {}))
                Return output
            End Get
        End Property

    End Class
    Public Class R4FormatCollection
        Implements SaveFormatCollection

        Public ReadOnly Property SaveFormats As System.Collections.Generic.List(Of SaveFormat) Implements SaveFormatCollection.SaveFormats
            Get
                Dim output As New System.Collections.Generic.List(Of SaveFormat)
                output.Add(New RawFormat("R4", "sav", {512 * 1024}))
                output.Add(New RawFormat("CycloDS", "sav", {512 * 1024}))
                output.Add(New RawFormat("EDGE", "sav", {512 * 1024}))
                output.Add(New RawFormat("M3 Simply", "sav", {512 * 1024}))
                output.Add(New RawFormat("TTDS (i)", "sav", {512 * 1024}))
                output.Add(New RawFormat("Supercard CF", "sav", {512 * 1024}))
                output.Add(New RawFormat("Supercard SD", "sav", {512 * 1024}))
                output.Add(New RawFormat("Ninja DS", "sav", {512 * 1024}))
                output.Add(New RawFormat("M3 DS Real", "0", {512 * 1024}))
                output.Add(New RawFormat("G6 DS Real", "0", {512 * 1024}))
                output.Add(New RawFormat("M3i Zero", "sav", {512 * 1024, 1024 * 1024}))
                Return output
            End Get
        End Property
    End Class
End Class
Public Class SizeManager
    Public Shared Function GetStringFromInteger(ByVal Bytes As Long) As String
        If Bytes < 1024 Then
            Return Bytes & " Bytes"
        ElseIf Bytes < (1024 * 1024) Then
            Return (Bytes / 1024) & " Kilo-Bytes - " & GetBitsStringFromInteger(Bytes)
        ElseIf Bytes < (1024 * 1024 * 1024) Then
            Return (Bytes / (1024 * 1024)) & " Mega-Bytes - " & GetBitsStringFromInteger(Bytes)
        Else
            Return (Bytes / (1024 * 1024 * 1024)) & " Giga-Bytes - " & GetBitsStringFromInteger(Bytes) '(8589934592)
        End If
    End Function
    Public Shared Function GetBitsStringFromInteger(ByVal Bytes As Long) As String
        If Bytes * 8 < (1024) Then
            Return Bytes * 8 & " Bits"
        ElseIf Bytes * 8 < (1024 ^ 2) Then
            Return (Bytes * 8 / 1024) & " Kilo-Bits"
        ElseIf Bytes * 8 < (1024 ^ 3) Then
            Return (Bytes * 8 / (1024 ^ 2)) & " Mega-Bits"
        Else
            Return (Bytes * 8 / (1024 ^ 3) & " Giga-Bits")
        End If
    End Function
    Public Shared Function SizeArray() As String()
        Dim val As String() = {"Auto Detect", "0.5 Kilo-Bytes - 4 Kilo-Bits", "8 Kilo-Bytes - 64 Kilo-Bits", "32 Kilo-Bytes - 256 Kilo-Bits", "64 Kilo-Bytes - 512 Kilo-Bits", "128 Kilo-Bytes - 1 Mega-Bits", "256 Kilo-Bytes - 2 Mega-Bits", "512 Kilo-Bytes - 4 Mega-Bits", "1 Mega-Bytes - 8 Mega-Bits", "2 Mega-Bytes - 16 Mega-Bits", "4 Mega-Bytes - 32 Mega-Bits", "8 Mega-Bytes - 64 Mega-Bits", "16 Mega-Bytes - 128 Mega-Bits", "32 Mega-Bytes - 256 Mega-Bits", "64 Mega-Bytes - 512 Mega-Bits", "128 Mega-Bytes - 1 Giga-Bits", "256 Mega-Bytes - 2 Giga-Bits"}
        Return val
    End Function
    Public Shared Function Size(ByVal SizeString As String) As Long
        If SizeString.StartsWith("Auto") Then
            Return 0
        End If
        Dim parts As String() = SizeString.Split(" ")
        Dim output As Decimal = parts(0)
        Select Case parts(1)(0)
            Case "B"
                'do nothing, output is already accurate
            Case "K"
                output = output * 1024
            Case "M"
                output = output * 1024 * 1024
            Case "G"
                output = output * 1024 * 1024 * 1024
        End Select
        Return output
    End Function
End Class
Namespace DeSmuMe
    Friend Class DeSmuMe
        Implements Interfaces.SaveFormat
        Public Shared ReadOnly Property EmptyIntArray As Long()
            Get
                Dim output As Long() = {}
                Return output
            End Get
        End Property
        Public Function ToRaw(ByVal DSV As Byte()) As Byte() Implements Interfaces.SaveFormat.ToRaw
            Dim newfile(DSV.Length - 123) As Byte
            For count As Integer = 0 To newfile.Length - 1
                newfile(count) = DSV(count)
            Next
            Return newfile
        End Function
        Public Function ToType(ByVal RAW As Byte()) As Byte() Implements Interfaces.SaveFormat.ToType
            Dim footer As Byte() = My.Resources.dsvfooter
            Dim newfile(RAW.Length + (footer.Length - 1)) As Byte
            For count As Integer = 0 To RAW.Length - 1
                newfile(count) = RAW(count)
            Next
            For count As Integer = 0 To footer.Length - 1
                newfile((RAW.Length) + count) = footer(count)
            Next
            Return newfile
        End Function
        Property IsOnline As Boolean Implements Interfaces.SaveFormat.IsOnline
        Property Filename As String Implements Interfaces.SaveFormat.Filename
        Property ExtendedData As String Implements Interfaces.SaveFormat.ExtendedData
        Public ReadOnly Property FileExtension As String Implements Interfaces.SaveFormat.FileExtension
            Get
                Return "dsv"
            End Get
        End Property
        Public ReadOnly Property FileSize As Long() Implements Interfaces.SaveFormat.FileSize
            Get
                Return EmptyIntArray
            End Get
        End Property
        Public ReadOnly Property Name As String Implements Interfaces.SaveFormat.Name
            Get
                Return "DeSmuME"
            End Get
        End Property
        Public Overrides Function ToString() As String Implements SaveFormat.ToString
            Return Name
        End Function
        Public ReadOnly Property CanBeSource As Boolean Implements Interfaces.SaveFormat.CanBeSource
            Get
                Return True
            End Get
        End Property
        Public ReadOnly Property CanBeTarget As Boolean Implements Interfaces.SaveFormat.CanBeTarget
            Get
                Return True
            End Get
        End Property
    End Class
    Public Class Formats
        Implements Interfaces.SaveFormatCollection
        Public ReadOnly Property SaveFormats As Generic.List(Of Interfaces.SaveFormat) Implements Interfaces.SaveFormatCollection.SaveFormats
            Get
                Dim output As New List(Of Interfaces.SaveFormat)
                output.Add(New DeSmuMe)
                Return output
            End Get
        End Property
    End Class
End Namespace
Public Class ToStringComparer(Of T)
    Implements IComparer(Of T)
    Public Function Compare(ByVal x As T, ByVal y As T) As Integer Implements System.Collections.Generic.IComparer(Of T).Compare
        Return String.Compare(x.ToString, y.ToString)
    End Function
End Class